package flib;

import java.awt.CheckboxMenuItem;
import java.awt.Color;
import java.awt.Container;
import java.awt.Frame;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.Vector;

import processing.core.PApplet;
import processing.core.PGraphics;
import processing.core.PImage;
import processing.event.KeyEvent;
import simpleGeom.Rect2D;
import simpleGeom.Vec2D;
import simpleGui.FileHelper;
import simpleGui.Gui;
import simpleGui.GuiCheckButton;
import simpleGui.GuiCheckboxGroup;
import simpleGui.GuiClickButton;
import simpleGui.GuiComponent;
import simpleGui.GuiGraphics;
import simpleGui.GuiLabel;
import simpleGui.GuiScrollList;
import simpleGui.GuiStateButton;
import simpleGui.PCam2DGraphics;

public class FlibEditor extends PApplet implements ActionListener, ItemListener {
	static final int LINE = 0, POLYLINE = 1, SELECT = 2, MOVE=3, STAIR=4;
	static final int LAYER_VIS=1,LAYER_SNAP=2,LAYER_NONE=0;
	
	LinkedList history = new LinkedList();
	
	MenuBar myMenu;
	String path = "/Users/administrator/projekte/Doktor/References/PDF/Das Grundrisswerk scan/data/";
	String pathImageFolder = path + "img/";
	String pathOBJFolder = path + "obj/";
	String pathConfig = "/Users/administrator/Documents/workspace/FrameWork1308P2/flibConfig.xml";
	
	CheckboxMenuItemGroup menuGroupWall;
	CheckboxMenuItemGroup menuGroupRoom;
	GuiCheckboxGroup groupTool;
	GuiCheckboxGroup groupLevel;
	GuiCheckboxGroup groupBuilding;
	
	int editMode = 0;
	FlibHENode selectedNode;
	FlibHENode closestNode;
	FlibWall closestWall;
	FlibFace closestRoom;
	int stairMode=-1;
	
	Vec2D closestNodeLayer;
	
	FlibHENode firstNode;
	FlibHENode secondNode;
	Vec2D moveBackup;

	TreeMap<String, WallType> walltypes;
	TreeMap<String, RoomType> roomtypes;

	Vector<FlibWall> sel_Walls = new Vector<FlibWall>();
	Vector<FlibFace> sel_Rooms = new Vector<FlibFace>();
	Vector<FlibBuilding>flibBuildings =new Vector<FlibBuilding>();
	FlibBuilding buildingPreview;
	PCam2DGraphics cam;
	FlibLevel selectedLevel;
	PImage img;

	boolean flagSave;
	boolean flagLoad;
	boolean flagAddNode;
	boolean flagAddLine;
	boolean flagSelect;
	boolean flagMultiSelect;
	boolean flagDelete;
	boolean flagChangeMode;
	boolean flagChangeLevel;
	boolean flagChangeBuilding;
	boolean flagUndo;
	boolean flagMoveSelect;
	boolean flagMovePlace;
	boolean flagCancelLine;
	boolean flagZoomToBounds;
	boolean flagRound;
	boolean flagStair;

	WallType flagWallType;
	RoomType flagRoomType;
	
	float minNodeDist = 0.2f;// 1cm away from node or wall

	int pickingDist = 10;
	float xModel;
	float yModel;
	
	float strokeWidth=4;
	float scaledStroke;
	
	Gui gui;
	GuiScrollList scroll;

	Vector<String>files;
	String actualFile;
	
	CheckboxMenuItem checkBoxImage;
	
	TreeMap<String,FlibBuilding>buildings=new TreeMap<String,FlibBuilding>();
	FlibBuilding  building;
	Vector<GuiComponent>levelComponents=new Vector<GuiComponent>();
	HashMap<FlibLevel ,GuiStateButton> guiLevelVisibility;
	
	
	FloorplanLoader2D loader;
	boolean isDisplayGrid=true;
	boolean isDisplayImage=true;
	boolean isSnapGrid=true;
	boolean lineOrtho=false;
	PGraphics preview;
	public void setup() {
		size(1600, 900);
		smooth();
		cam = new PCam2DGraphics(this);
		cam.setScreen(200,100, width-400, height-200);
		createGraphForImages();
		loader=new FloorplanLoader2D(new File(pathConfig));
		walltypes = FloorplanLoader2D.loadWallTypes(new File(pathConfig));
		roomtypes = FloorplanLoader2D.loadRoomTypes(new File(pathConfig));
		
		walltypes =loader.walltypes;
		roomtypes =loader.roomtypes;
		
		
		setupMenu();
		setupGui();
		println("load buildings");
		setupBuildings();
		println("buildings loaded");
	}
	
	public void setupGui(){
		int btnW=50;
		int btnH=20;
		gui=new Gui(this);
		gui.addComponent(0, 0, 200, height);
		GuiCheckButton chkDraw=new GuiCheckButton(gui,0,0,btnW,btnH,false,"Draw");
		chkDraw.addItemListener(this);
		gui.addChildDisplay(chkDraw);
		GuiCheckButton chkMove=new GuiCheckButton(gui,btnW,0,btnW,btnH,false,"Move");
		chkMove.addItemListener(this);
		gui.addChildDisplay(chkMove);
		GuiCheckButton chkSelect=new GuiCheckButton(gui,2*btnW,0,btnW,btnH,false,"Select");
		chkSelect.addItemListener(this);
		gui.addChildDisplay(chkSelect);
		GuiCheckButton chkStair=new GuiCheckButton(gui,3*btnW,0,btnW,btnH,false,"Draw Stair");
		chkStair.addItemListener(this);
		gui.addChildDisplay(chkStair);
		//GuiComponent guibar=new GuiComponent(gui,3*btnW,0,width-3*btnW,btnH);
		groupTool=new GuiCheckboxGroup();
		groupTool.add(chkDraw);
		groupTool.add(chkMove);
		groupTool.add(chkSelect);
		groupTool.add(chkStair);
		
		groupLevel=new GuiCheckboxGroup();
		
		gui.addChildDisplay(new GuiComponent(gui,width-200,0,200,200));
		scroll=new GuiScrollList(gui,width-200,200,200,height-(200+btnH),200);
		GuiClickButton btnLoad=new GuiClickButton(gui,width-200,height-btnH,200,btnH,"Load");
		btnLoad.addActionListener(this);
		gui.addChildDisplay(btnLoad);
		
		Vector<File>imageFiles=FileHelper.getFilesFromFolder(pathImageFolder,".png");
		guiLevelVisibility=new HashMap<FlibLevel,GuiStateButton>();
		GuiGraphics guiGr=new GuiGraphics(gui,width-200,0,200,200);
		preview=guiGr.getCanvas();
		//HashMap<String,>
		//gui.addChildDisplay(guiGr);
	}
	public void setupMenu(){
		myMenu = new MenuBar();
		Menu menuFile = new Menu("File");
		MenuItem menuLoad=new MenuItem("Load");
		menuLoad.addActionListener(this);
		menuFile.add(menuLoad);
		MenuItem menuSave=new MenuItem("Save");
		menuSave.addActionListener(this);
		menuFile.add(menuSave);
		myMenu.add(menuFile);
		
		Menu menuEdit = new Menu("Edit");
		MenuItem menuDelete=new MenuItem("Delete");
		menuDelete.addActionListener(this);
		menuEdit.add(menuDelete);
		MenuItem menuClear=new MenuItem("Clear");
		menuClear.addActionListener(this);
		menuEdit.add(menuClear);
		MenuItem menuRound=new MenuItem("Round");
		menuRound.addActionListener(this);
		menuEdit.add(menuRound);
		myMenu.add(menuEdit);
		
		
		//menuGroupWall=new CheckboxMenuItemGroup();
		Menu menuWallType = new Menu("Wall");
		for (String key : walltypes.keySet()) {
			MenuItem checkBoxMenuItem=new MenuItem(key);
			//menuGroupWall.add(checkBoxMenuItem);
			checkBoxMenuItem.addActionListener(this);
			menuWallType.add(checkBoxMenuItem);
		}
		myMenu.add(menuWallType);
		
		//menuGroupRoom=new CheckboxMenuItemGroup();
		
		Menu menuRoomType = new Menu("Room");
		for (String key : roomtypes.keySet()) {
			MenuItem checkBoxMenuItem=new MenuItem(key);
			//menuGroupRoom.add(checkBoxMenuItem);
			checkBoxMenuItem.addActionListener(this);
			menuRoomType.add(checkBoxMenuItem);
		}
		myMenu.add(menuRoomType);
		
		Menu menuDisplay = new Menu("Display");
		checkBoxImage=new CheckboxMenuItem("Image");
		checkBoxImage.addItemListener(this);
		checkBoxImage.setState(true);
		menuDisplay.add(checkBoxImage);
		CheckboxMenuItem checkBoxGrid=new CheckboxMenuItem("Grid");
		checkBoxGrid.addItemListener(this);
		checkBoxGrid.setState(isDisplayGrid);
		menuDisplay.add(checkBoxGrid);
		myMenu.add(menuDisplay);
		
		Menu menuSnap = new Menu("Snap");
		CheckboxMenuItem checkBoxGridSnap=new CheckboxMenuItem("Snap Grid");
		checkBoxGridSnap.addItemListener(this);
		menuSnap.add(checkBoxGridSnap);
		myMenu.add(menuSnap);
		
		findFrame().setMenuBar(myMenu);
	}

	public GuiComponent constructLevelGui(int x,int y,int w,int h,FlibLevel level){
		GuiComponent comp=new GuiComponent(gui,x,y,w,h);
		GuiStateButton checkBoxVisible=new GuiStateButton(gui,x,y,20,20,1,3,level.filename); 
		checkBoxVisible.setDisplayLabel(false);
		guiLevelVisibility.put(level,checkBoxVisible);
		GuiCheckButton checkBoxSelected=new GuiCheckButton(gui,x+20,y,180,20,false,level.filename); 
		checkBoxSelected.setDisplayLabel(true);
		checkBoxSelected.addItemListener(this);
		groupLevel.add(checkBoxSelected);
		comp.addComponent(checkBoxVisible);
		comp.addComponent(checkBoxSelected);
		levelComponents.add(comp);
		return comp;
	}
	
	public void createGraphForImages(){
		Vector<File>imageFiles=FileHelper.getFilesFromFolder(pathImageFolder,".png");
		Vector<File>objFiles=FileHelper.getFilesFromFolder(pathOBJFolder,".obj");
		for (int i=0;i<imageFiles.size();i++){
			File file=imageFiles.get(i);
			String name=file.getName();
			name=name.replace(".png", "");
			File fileOBJ=new File(pathOBJFolder+name+".obj");
			try {
				fileOBJ.createNewFile();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	public void setupBuildings(){
		buildings=loader.loadBuildingsFromOBJ(pathOBJFolder);
		//buildings=loadBuildings(pathOBJFolder);
		Vector<String>labels=new Vector<String>();
		for(Map.Entry<String,FlibBuilding> entry : buildings.entrySet()) {
			  String key = entry.getKey();
			  FlibBuilding value = entry.getValue();
			  labels.add(key);
		}
		scroll.setList(labels);
		groupBuilding=new GuiCheckboxGroup();
		for (int i=0;i<scroll.buttonList.size();i++){
			GuiCheckButton box=scroll.buttonList.get(i);
			box.addItemListener(this);
			groupBuilding.add(box);
		}
		Entry<String,FlibBuilding> entry=buildings.firstEntry();
		loadBuilding(entry.getKey());
	}
	public void loadBuilding(String label){
		println("label:"+label);
		// add Gui
		 building=buildings.get(label);
		// remove all gui elements;
		for (int i=0;i<levelComponents.size();i++){
			GuiComponent gc=levelComponents.get(i);
			for (int ii=0;ii<gc.children.size();ii++){
				GuiComponent child=gc.children.get(ii);
				child.unregister();
			}
			gc.unregister();
		}
		guiLevelVisibility.clear();
		groupLevel=new GuiCheckboxGroup();
		for (int i=0;i<building.levels.size();i++){
			FlibLevel level=building.levels.get(i);
			String imgName=level.filename.replace("obj","png");
			PImage img=this.loadImage(pathImageFolder+imgName);
			//img.filter(INVERT);
			level.setImage(img);
			this.constructLevelGui(0, i*20+20, 200, 20, level);
		}
		groupLevel.getGuiCheckButton(building.levels.firstElement().filename).state=true;
		selectLevel();
	}
	
	public void selectLevel(){
		println("select Level");
		firstNode=null;
		if (selectedLevel!=null){
			selectedLevel.removeEmptyNodes();
		}
		deselect();
		GuiCheckButton box=groupLevel.getSelectedItem();
		println(box.getLabel());
		selectedLevel=building.levelMap.get(box.getLabel());
		img=selectedLevel.image;
		cam.setBounds(0, 0, img.width, img.height);
		flagZoomToBounds=true;
	}
	
	public Frame findFrame() {
		Container f = this.getParent();
		while (!(f instanceof Frame) && f != null)
			f = f.getParent();
		return (Frame) f;
	}

	public void draw() {
	
		if (flagZoomToBounds){
			cam.zoomToBounds();
			flagZoomToBounds=false;
		}
		
		xModel = cam.getModelX(mouseX);
		yModel = cam.getModelY(mouseY);
		
		snap();
		findClosest();
		
		if (firstNode!=null){
			if (lineOrtho){
				float dX=xModel-firstNode.x;
				float dY=yModel-firstNode.y;
				if (abs(dX)>abs(dY)){
					yModel=firstNode.y;
				}
				else{
					xModel=firstNode.x;
				}
			}
		}
		if (flagStair){
			println("stairMode: "+stairMode);
			if (stairMode==0){
				if (closestWall!=null){
					closestWall.type=walltypes.get("stair-down");
					stairMode++;
				}
			}
			else if (stairMode==1){
				if (closestRoom!=null){
					closestRoom.type=roomtypes.get("area-stair");
					stairMode++;
				}
			}
			else if (stairMode==2){
				if (closestWall!=null){
					closestWall.type=walltypes.get("stair-top");
					println("label: "+closestWall.type.label);
					stairMode=0;
				}
			}
			flagStair=false;
		}
		
		assignTypes();
		if (flagCancelLine){
			selectedLevel.removeEmptyNodes();
			flagCancelLine=false;
		}
		if (flagLoad){
			GuiCheckButton bt=groupBuilding.getSelectedItem();
			//loadImg(pathImageFolder+bt.getLabel());
			//load(pathOBJFolder+"test.obj");
			
			loadBuilding(bt.getLabel());
			flagLoad=false;
		}
		if (flagSave){
			saveBuilding();
			flagSave=false;
		}
		if (flagUndo){
			firstNode=null;
			deselect();
			pollHistory();
			flagUndo=false;
		}
		if (flagChangeBuilding){
			GuiCheckButton btn=groupBuilding.getSelectedItem();
			FlibBuilding building=buildings.get(btn.getLabel());
			displayPreview(building);
			flagChangeBuilding=false;
		}
		if (flagChangeLevel){
			selectLevel();
			flagChangeLevel=false;
		}
		if (flagChangeMode){
			if (editMode!=LINE){
				firstNode=null;
			}
			else if (editMode==LINE){
				deselect();
			}
			flagChangeMode=false;
		}
		if (flagAddNode) {
			if (firstNode == null) {
				firstNode = addNode(xModel, yModel);
			} else {
				
				FlibHENode secondNode = addNode(xModel, yModel);
				if (secondNode != firstNode) {
					addWall(firstNode, secondNode);
					firstNode = secondNode;
				}
			}
			flagAddNode = false;
		}
		if (flagMoveSelect){
			if (closestNode!=null){
				selectedNode=closestNode;
				moveBackup=new Vec2D(selectedNode.x,selectedNode.y);
			}
			flagMoveSelect=false;
		}
		if (flagMovePlace){
			move();
			flagMovePlace=false;
		}
		if (selectedNode!=null){
			selectedNode.x=xModel;
			selectedNode.y=yModel;
		}
		if (flagSelect) {
			select();
			flagMultiSelect = false;
			flagSelect = false;
		}
		if (flagDelete) {
			delete();
			flagDelete = false;
		}
		if (flagSave) {
			flagSave = false;
		}
		if (flagLoad) {
			flagLoad = false;
		}
		
		display();
	}
	public void round(FlibLevel level){
		for (int i=0;i<level.nodes.size();i++){
			FlibHENode n=level.nodes.get(i);
			n.x=getRound(n.x);
			n.y=getRound(n.y);
		}
	}
	
	public float getRound(float in){
		return round(in / 10) * 10;
	}
	public void snap() {
		
		// snapping
		if (isSnapGrid) {
			xModel = getRound(xModel);
			yModel =  getRound(yModel);
		}
		closestNodeLayer=null;
		// snapp other layers
		float minD = Float.MAX_VALUE;
		float[] closest = new float[2];
		for (int i = 0; i < building.levels.size(); i++) {
			FlibLevel level = building.levels.get(i);
			GuiStateButton btn = guiLevelVisibility.get(level);
			if (btn.state == LAYER_SNAP) {
				FlibHENode n = level.getClosestNode2D(xModel, yModel);
				if (n != null) {
					float cD = n.dist(xModel, yModel);
					if (cD < minD) {
						minD = cD;
						closest[0] = n.x;
						closest[1] = n.y;
					}
				}
			}
		}
		if (minD * cam.scale < this.pickingDist) {
			
			xModel = closest[0];
			yModel = closest[1];
			closestNodeLayer=new Vec2D(xModel,yModel);
		} else {
			for (int i = 0; i < building.levels.size(); i++) {
				FlibLevel level = building.levels.get(i);
				GuiStateButton btn = guiLevelVisibility.get(level);
				if (btn.state == LAYER_SNAP) {

					FlibWall closestWall = level.getClosestWall2D(xModel,
							yModel);
					if (closestWall != null) {
						float cD = closestWall.distToSeg(xModel, yModel);
						if (cD < minD) {
							minD = cD;
							float[] cClose = closestWall.getClosestPointToSeg(
									xModel, yModel, null);
							closest[0] = cClose[0];
							closest[1] = cClose[1];
						}
					}
				}
			}
			if (minD * cam.scale < this.pickingDist) {
				xModel = closest[0];
				yModel = closest[1];
				closestNodeLayer=new Vec2D(xModel,yModel);
			}
		}
		
	}
	public void display() {
		pushMatrix();
		cam.useCam(g);
		background(255);
		stroke(0,0,255);
		scaledStroke=strokeWidth/cam.scale;
		if (selectedLevel != null) {
			if (img!=null&& isDisplayImage){
				image(img,0,0);
				// g.removeCache(img);
			}
			fill(100);
			noStroke();
			for (int i = 0; i < selectedLevel.rooms.size(); i++) {
				FlibFace room = selectedLevel.rooms.get(i);
				if (room.type!=null){
					Color c=room.type.color;
					if (room.type.label.contains("stair")){
						fill(0,0,255,100);
					}
					else{
					fill(c.getRed(),c.getGreen(),c.getBlue(),100);
					}
					//fill(room.type.color.getRGB());
				}
				room.display(g);
			}
			
			
			for (int i = 0; i < sel_Rooms.size(); i++) {
				fill(255,0,0,100);
				FlibFace room = sel_Rooms.get(i);
				room.display(g);
			}
			
			if (closestRoom!=null){
				fill(255,0,0,100);
				closestRoom.display(g);
			}
			for (int i=0;i<building.levels.size();i++){
				FlibLevel level=building.levels.get(i);
				GuiStateButton gBtn=guiLevelVisibility.get(level);
				if (level != selectedLevel && gBtn.getState() != LAYER_NONE) {
					stroke(200);
					strokeWeight(scaledStroke);
					for (int j = 0; j < level.walls.size(); j++) {
						FlibWall wall = level.walls.get(j);
						line(wall.x1(), wall.y1(), wall.x2(), wall.y2());
					}
					if (closestNodeLayer != null) {
						ellipse(closestNodeLayer.x, closestNodeLayer.y, 10, 10);
					}
				}
			}
		
			strokeWeight(scaledStroke*2f);
			stroke(255, 0, 0);
			for (int i = 0; i < sel_Walls.size(); i++) {
				FlibWall wall = sel_Walls.get(i);
				wall.display2D(g);
			}
			stroke(0,0,255);
			strokeWeight(scaledStroke);
			for (int i = 0; i < selectedLevel.walls.size(); i++) {
				FlibWall wall = selectedLevel.walls.get(i);
				stroke(0,255,0);
				displayWall(g,wall);
			}
			
			if (firstNode != null) {
				line(firstNode.x, firstNode.y, xModel, yModel);
			}
			
			for (int i = 0; i < selectedLevel.nodes.size(); i++) {
				FlibHENode node = selectedLevel.nodes.get(i);
				fill(0,255,0);
				if (!node.checkSequence())fill(255,0,0);
				ellipse(node.x, node.y, 10, 10);
			}
			
			stroke(255, 0, 0,200);
			if (closestNode != null) {
				strokeWeight(scaledStroke*6f);
				point(closestNode.x, closestNode.y);
				//ellipse(, 30, 30);
			}
			
			if (closestWall != null) {
				strokeWeight(scaledStroke*2f);
				closestWall.display2D(g);
				strokeWeight(scaledStroke*4f);
				float[] result =closestWall.getClosestPointToSeg(xModel, yModel, null);
				point(result[0], result[1]);
			}
		}
		
		// draw grid
		if (isDisplayGrid&cam.scale>0.5f){
			g.noFill();
			g.stroke(0,0,255,100);
			g.strokeWeight(1f/cam.scale);
			float gX1=cam.getModelX(0);
			float gY1=cam.getModelY(0);
			float gX2=cam.getModelX(width);
			float gY2=cam.getModelY(height);
			gX1=round(gX1/10f)*10;
			gY1=round(gY1/10f)*10;
			float cX=gX1;
			while(cX<gX2){
				line(cX,gY1,cX,gY2);
				cX+=10;
			}
			float cY=gY1;
			while(cY<gY2){
				line(gX1,cY,gX2,cY);
				cY+=10;
			}
		}
		popMatrix();
		noStroke();
		fill(200,100);
	
		gui.display(g);
		// info
	
		
		int iPos=0;
		
		for (int i = 0; i < selectedLevel.rooms.size(); i++) {
			FlibFace room = selectedLevel.rooms.get(i);
			if (room.getArea2D()>0.05)iPos++;
			//println(room.getArea2D());
			
		}
		
	}
	public void displayPreview(FlibBuilding building){
		Rect2D r=building.getBounds2D();
		r.x1-=100;
		r.y1-=100;
		r.x2+=100;
		r.y2+=100;
		float dim=r.getMaxDimension();
		float scale=preview.width/dim;
		preview.beginDraw();
		preview.background(color(0));
		preview.pushMatrix();
		preview.scale(scale);
		preview.strokeWeight(1f/scale);
		preview.translate(-r.getCenterX()+(preview.width/2)/scale,-r.getCenterY()+(preview.height/2)/scale);
		preview.stroke(255);
		preview.fill(100);
		for (int i=0;i<building.levels.size();i++){
			FlibLevel level=building.levels.get(i);
			for (int j=0;j<level.rooms.size();j++){
				FlibFace w=level.rooms.get(j);
				w.display(preview);
			}
			for (int j=0;j<level.walls.size();j++){
				FlibWall w=level.walls.get(j);
				displayWall(preview,w);
				//preview.line(w.x1(), w.y1(), w.x2(), w.y2());
			}
		}
		preview.popMatrix();
		preview.endDraw();
	}
	public void displayStairEdge(PGraphics g,FlibHEdge hE,boolean up){
		FlibFace face= hE.getFace();
		float vx=hE.x2()-hE.x1();
		float vy=hE.y2()-hE.y1();
		float mag=sqrt(vx*vx+vy*vy);
		if (face!=null&&face.getLabel().equals("area-stair")){
			float nX = -20f * vy / mag;
			float nY = 20f * vx / mag;
			float cX = hE.cX();
			float cY = hE.cY();
			if (up){
				displayArrow(g,cX + nX, cY + nY,cX , cY );
			}
			else{
				displayArrow(g,cX , cY , cX + nX, cY + nY);
			}
		}
	}
	public void displayArrow(PGraphics g,float x1,float y1,float x2,float y2){
		float dX=y2-y1;
		float dY=-x2+x1;
		g.beginShape(TRIANGLES);
		g.vertex(x1-dX,y1-dY);
		g.vertex(x1+dX,y1+dY);
		g.vertex(x2,y2);
		g.endShape();
	}
	
	public void displayWall(PGraphics g,FlibWall wall){
		float vx=wall.x2()-wall.x1();
		float vy=wall.y2()-wall.y1();
		float mag=sqrt(vx*vx+vy*vy);
		if (wall.type.label.equals("wall-help")){
			g.stroke(255,0,0);
			line(wall.x1(), wall.y1(), wall.x2(), wall.y2());
			return;
		}
		line(wall.x1(), wall.y1(), wall.x2(), wall.y2());
		
		if (wall.isHelper()){
			int n=(int)(mag/10f);
			float dX=vx/n;
			float dY=vy/n;
			float x1=wall.x1();
			float y1=wall.y1();
			
			for (int i=0;i<n;i++){
				float x2=x1+dX;
				float y2=y1+dY;
				if (i%2==0){
				g.line(x1,y1,x2,y2);
				}
				x1=x2;
				y1=y2;
			}
		
			if (wall.type.label.equals("stair-top")){
				displayStairEdge(g,wall.hEdge,true);
				displayStairEdge(g,wall.hEdge.getSym(),true);
			}
			if (wall.type.label.equals("stair-down")){
				displayStairEdge(g,wall.hEdge,false);
				displayStairEdge(g,wall.hEdge.getSym(),false);
			}
		}
		
		else {
			
			
			if (wall.isPermeable()) {
				float nX = 20f * vy / mag;
				float nY = -20f * vx / mag;
				float cX = wall.cX();
				float cY = wall.cY();
				if (wall.type.label.equals("door-entrance")){
					displayArrow(g,cX - nX, cY - nY,cX,cY);
					displayArrow(g,cX + nX, cY + nY,cX,cY);
				}
				else{
					g.line(cX - nX, cY - nY, cX + nX, cY + nY);
				}
			};
		
			if (wall.isTransparent()) {
				float nX = 10f * vy / mag;
				float nY = -10f * vx / mag;
				float cX = wall.cX();
				float cY = wall.cY();
				g.line(wall.x1() - nX, wall.y1() - nY, wall.x2() - nX, wall.y2()
						- nY);
				g.line(wall.x1() + nX, wall.y1() + nY, wall.x2() + nX, wall.y2()
						+ nY);
			} else {
				g.line(wall.x1(), wall.y1(), wall.x2(), wall.y2());
			}
		}
		
	}
	public void findClosest() {
		if (selectedLevel != null) {
			closestNode = selectedLevel.getClosestNode2D(xModel, yModel);
			if (closestNode != null) {
				float dist = closestNode.dist(xModel, yModel);
				if (dist * cam.scale > pickingDist) {
					closestNode = null;
				}
			}

			closestWall = selectedLevel.getClosestWall2D(xModel, yModel);
			if (closestWall != null) {
				float dist = closestWall.distToSeg(xModel, yModel);
				if (dist * cam.scale > pickingDist) {
					closestWall = null;
				}
			}

			closestRoom = selectedLevel.getContainingRoom2D(xModel, yModel);
		}
	}
	
	
	
	
	public void saveBuilding() {
		for (int i=0;i<building.levels.size();i++){
			FlibLevel level=building.levels.get(i);
			FloorplanLoader2D.saveAsGraph(level, new File(pathOBJFolder+level.filename));
		}
	}

	public void pollHistory() {
		FlibLevel old=(FlibLevel) history.pollFirst();
		if (old!=null){	
			selectedLevel.construct(old);
		}
	}

	public void pushHistory() {
		if (history.size()>20){
			history.removeLast();
		}
		println("history.size()"+history.size());
		FlibLevel backup=new FlibLevel(selectedLevel);
		
		backup.removeEmptyNodes();
		history.push(backup);
	}

	public FlibHENode addNode(float x, float y) {
		// if its a Node return it
		// FlibHENode closestNode=selectedLevel.getClosestNode2D(x,y);
		pushHistory();
		if (closestNode != null) {
			return closestNode;
		}
		// if its onLine: split HEdge

		if (closestWall != null) {
			float wallD = closestWall.distToSeg(x, y);
			float[] result = new float[2];
			closestWall.getClosestPointToSeg(x, y, result);
			FlibHENode node = selectedLevel.addNode(result[0], result[1]);
			FlibWall wall=selectedLevel.splitWall(closestWall, node);
			return node;
		}
		
		FlibHENode newN=selectedLevel.addNode(x, y);
		
		return newN;
	}

	public void addWall(FlibHENode n1, FlibHENode n2) {
		Vector<FlibWall>newWalls=selectedLevel.addWallIntersect(n1, n2);
		for (int i=0;i<newWalls.size();i++){
			sel_Walls.add(newWalls.get(i));
		}
		pushHistory();
	}
	
	public void assignTypes(){
		if (flagWallType!=null){
			for (int i=0;i<sel_Walls.size();i++){
				sel_Walls.get(i).type=flagWallType;
			}
			flagWallType=null;
			pushHistory();
		}
		if (flagRoomType!=null){
			for (int i=0;i<sel_Rooms.size();i++){
				sel_Rooms.get(i).type=flagRoomType;
			}
			flagRoomType=null;
			pushHistory();
		}
	}
	
	public void move(){
		if (selectedNode!=null&&!selectedLevel.valid(selectedNode)){
			selectedNode.x=moveBackup.x;
			selectedNode.y=moveBackup.y;
		}
		selectedNode=null;
	}
	
	public void delete() {
		pushHistory();
		if (selectedLevel != null) {
			for (int i = 0; i < sel_Walls.size(); i++) {
				FlibWall wall = sel_Walls.get(i);
				selectedLevel.removeWall(wall);
			}
			sel_Walls.clear();
			sel_Rooms.clear();
			
		}
	}
	
	public void deselect(){
		sel_Walls.clear();
		sel_Rooms.clear();
	}
	
	public void select() {
		boolean wallIsSelected = sel_Walls.contains(closestWall);
		boolean roomIsSelected = sel_Rooms.contains(closestRoom);
		if (!flagMultiSelect) {
			deselect();
		}
		if (closestWall != null) {
			if (!wallIsSelected)
				sel_Walls.add(closestWall);
			else
				sel_Walls.remove(closestWall);
		} else if (closestRoom != null) {
			if (!roomIsSelected)
				sel_Rooms.add(closestRoom);
			else
				sel_Rooms.remove(closestRoom);
		}
	}

	@Override
	public void mouseDragged(MouseEvent e) {
		// TODO Auto-generated method stub
		super.mouseDragged(e);
	}

	@Override
	public void mouseMoved(MouseEvent e) {
		// TODO Auto-generated method stub
		super.mouseMoved(e);
	}

	@Override
	public void mousePressed(MouseEvent e) {
		// TODO Auto-generated method stub
		if (gui.contains(e.getX(), e.getY()))
			return;
		if (!cam.isDragMode() && !e.isAltDown() && !e.isControlDown()) {
			if (editMode == SELECT) {
				flagSelect = true;
				if (e.isShiftDown()) {
					flagMultiSelect = true;
				}
			}
			if (editMode == MOVE) {
				flagMoveSelect = true;
			}
			if (editMode == STAIR){
				flagStair=true;
			}
		}
		if (!e.isShiftDown()) {
			super.mousePressed(e);
		}
	}



	@Override
	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub
		if (gui.isVisible()&&gui.contains(e.getX(), e.getY()))return;
		if (!cam.isDragMode() && !e.isAltDown() && !e.isControlDown()
				) {
			if (editMode == LINE) {
				flagAddNode = true;
			}
		}
		if (editMode==MOVE){
			flagMovePlace=true;
		}
		super.mouseReleased(e);
	}

	@Override
	public void itemStateChanged(ItemEvent e) {
		// TODO Auto-generated method stub
		Object src=e.getSource();
		if (CheckboxMenuItem.class.equals(src.getClass())){
			CheckboxMenuItem chkBox=(CheckboxMenuItem)src;
			if (chkBox.getLabel().equals("Grid")){
				isDisplayGrid=chkBox.getState();
			}
			if (chkBox.getLabel().equals("Snap Grid")){
				isSnapGrid=chkBox.getState();
			}
			if (chkBox.getLabel().equals("Image")){
				isDisplayImage=chkBox.getState();
			}
		}
		if (e.getStateChange() == ItemEvent.SELECTED) {
			/*if (menuGroupWall.items.contains(src)){
				flagWallType=walltypes.get(((CheckboxMenuItem)src).getLabel());
			}
			else if (menuGroupRoom.items.contains(src)){
				flagRoomType=roomtypes.get(((CheckboxMenuItem)src).getLabel());
			}*/
			 if (groupTool.containsItem(src)){
				GuiCheckButton btn=(GuiCheckButton)src;
				flagChangeMode=true;
				if (btn.getLabel().equals("Draw")){
					editMode = LINE;
				}
				if (btn.getLabel().equals("Select")){
					editMode = SELECT;
				}
				if (btn.getLabel().equals("Move")){
					editMode = MOVE;
				}
				if (btn.getLabel().equals("Draw Stair")){
					println("stairMode");
					editMode = STAIR;
					stairMode=0;
				}
			}
			else if (groupLevel.containsItem(src)){
				GuiCheckButton btn=(GuiCheckButton)src;
				println("flagChangeLevel");
				flagChangeLevel=true;
			}
			else if (groupBuilding.containsItem(src)){
				GuiCheckButton btn=(GuiCheckButton)src;
				println("flagChangeLevel");
				flagChangeBuilding=true;
				//flagLoad=true;
			}
		}
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		// TODO Auto-generated method stub
		if (arg0.getSource().getClass().equals(MenuItem.class)){
			MenuItem item=(MenuItem)arg0.getSource();
			if (item.getLabel().equals("Load")){
				flagLoad=true;
			}
			if (item.getLabel().equals("Save")){
				flagSave=true;
			}
			if (item.getLabel().equals("Delete")){
				flagDelete=true;
			}
			if (item.getLabel().equals("Select All")){
				//flagSelectAll=true;
			}
			if (item.getLabel().equals("Round")){
				round(selectedLevel);
				flagRound=false;
			}
			flagRoomType=roomtypes.get(item.getLabel());
			flagWallType=walltypes.get(item.getLabel());
		}
		if (arg0.getSource().getClass().equals(GuiClickButton.class)){
			GuiClickButton item=(GuiClickButton)arg0.getSource();
			if (item.getLabel().equals("Load")){
				flagLoad=true;
			}
		}
	}
	
	@Override
	public void keyPressed() {
		// TODO Auto-generated method stub
		if (key == this.ESC) {
			firstNode = null;
			if (editMode==LINE){
				flagCancelLine=true;
			}
		}
		
		if (keyCode==SHIFT){
			lineOrtho=true;
			println("lineOrtho: "+lineOrtho);
		}
		
		
		if (key == '1') 
			groupTool.getItems().get("Select").flipState();
		if (key == '2') 
			groupTool.getItems().get("Draw").flipState();	
		if (key == '3') 
			groupTool.getItems().get("Move").flipState();
		
		if (key == 'z') {
			flagChangeMode=true;
			flagUndo=true;
		}
		if (key == DELETE || key == BACKSPACE) {
			flagDelete = true;
		}
		key = 0;
	}

	
	public class CheckboxMenuItemGroup implements ItemListener {
		private HashSet<CheckboxMenuItem> items = new HashSet<CheckboxMenuItem>();
		public void add(CheckboxMenuItem cbmi) {
			cbmi.addItemListener(this);
			cbmi.setState(false);
			items.add(cbmi);
		}
		@Override
		public void itemStateChanged(ItemEvent e) {
			if (e.getStateChange() == ItemEvent.SELECTED) {
				Object itemAffected = e.getSource();
				for (CheckboxMenuItem item : items) {
					if (item != itemAffected)
						item.setState(false);
				}
			}
		}
		public void selectItem(CheckboxMenuItem itemToSelect) {
			for (CheckboxMenuItem item : items) {
				item.setState(item == itemToSelect);
			}
		}
		public CheckboxMenuItem getSelectedItem() {
			for (CheckboxMenuItem item : items) {
				if (item.getState())
					return item;
			}
			return null;
		}
	}


	@Override
	public void keyReleased(java.awt.event.KeyEvent e) {
		// TODO Auto-generated method stub
		
		super.keyReleased(e);
		if (keyCode==SHIFT){
			lineOrtho=false;
			println("lineOrtho: "+lineOrtho);
		}
	}
}
